package judge.service.impl;

import api.domain.dto.JudgeSubmitDTO;
import api.domain.vo.UserExeResultVO;
import api.domain.vo.UserQuestionResultVO;
import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import core.constants.Constant;
import core.constants.JudgeConstant;
import core.enums.CodeRunStatus;
import core.enums.QuestionResult;
import judge.domain.SandBoxExecuteResult;
import judge.domain.UserSubmit;
import judge.mapper.UserSubmitMapper;
import judge.service.IJudgeService;
import judge.service.ISandBoxPoolService;
import judge.service.ISandBoxService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class JudgeServiceImpl implements IJudgeService {

    @Autowired
    private ISandBoxService sandboxService;

    @Autowired
    private ISandBoxPoolService sandboxPoolService;

    @Autowired
    private UserSubmitMapper userSubmitMapper;
    @Override
    public UserQuestionResultVO doJudgeJavaCode(JudgeSubmitDTO judgeSubmitDTO) {
        UserQuestionResultVO userQuestionResultVO = new UserQuestionResultVO();
        SandBoxExecuteResult sandboxExecuteResult =
                sandboxPoolService.exeJavaCode(judgeSubmitDTO.getUserId(), judgeSubmitDTO.getUserCode(), judgeSubmitDTO.getInputList());
        if(sandboxExecuteResult != null && CodeRunStatus.SUCCEED
                .equals(sandboxExecuteResult.getRunStatus()))
            doJudge(judgeSubmitDTO, sandboxExecuteResult, userQuestionResultVO);
        else {
            userQuestionResultVO.setPass(QuestionResult.ERROR.getResult());
            if(sandboxExecuteResult != null) {
                userQuestionResultVO.setExeMessage(sandboxExecuteResult.getExeMessage());
            } else {
                userQuestionResultVO.setExeMessage(CodeRunStatus.UNKNOWN_FAILED.getMessage());
            }
            userQuestionResultVO.setUserExeResultVOList(null);
            userQuestionResultVO.setScore(JudgeConstant.ERROR_SCORE);
        }
        saveUserSubmit(judgeSubmitDTO, userQuestionResultVO);
        return userQuestionResultVO;
    }

    private void doJudge(JudgeSubmitDTO judgeSubmitDTO, SandBoxExecuteResult sandboxExecuteResult, UserQuestionResultVO userQuestionResultVO) {
        // 判断输出是否正确, 判断时间限制和空间限制
        List<String> exeOutputList = sandboxExecuteResult.getOutputList();
        List<String> outputList = judgeSubmitDTO.getOutputList();
        if(exeOutputList.size() == outputList.size()) {
            boolean isAllPass = true;
            List<UserExeResultVO> userExeResultVOList = new ArrayList<>();
            for (int i = 0; i < exeOutputList.size(); i++) {
                UserExeResultVO userExeResultVO = new UserExeResultVO();
                String exeOutput = exeOutputList.get(i);
                String output = outputList.get(i);
                userExeResultVO.setInput(judgeSubmitDTO.getInputList().get(i));
                userExeResultVO.setOutput(output);
                userExeResultVO.setExeOutput(exeOutput);
                userExeResultVOList.add(userExeResultVO);
                if(!exeOutput.equals(output)) {
                    isAllPass = false;
                }
            }
            userQuestionResultVO.setUserExeResultVOList(userExeResultVOList);
            if(!isAllPass){
                userQuestionResultVO.setPass(QuestionResult.ERROR.getResult());
                userQuestionResultVO.setScore(JudgeConstant.ERROR_SCORE);
                userQuestionResultVO.setExeMessage(CodeRunStatus.NOT_ALL_PASSED.getMessage());
                return;
            }
            // 判断时间限制和空间限制
            if(sandboxExecuteResult.getUseMemory() > judgeSubmitDTO.getSpaceLimit()){
                userQuestionResultVO.setPass(QuestionResult.ERROR.getResult());
                userQuestionResultVO.setScore(JudgeConstant.ERROR_SCORE);
                userQuestionResultVO.setExeMessage(JudgeConstant.OUT_OF_MEMORY);
                return;
            }
            if(sandboxExecuteResult.getUseTime() > judgeSubmitDTO.getTimeLimit()){
                userQuestionResultVO.setPass(QuestionResult.ERROR.getResult());
                userQuestionResultVO.setScore(JudgeConstant.ERROR_SCORE);
                userQuestionResultVO.setExeMessage(JudgeConstant.OUT_OF_TIME);
                return;
            }
            // 全部通过且资源限制都满足
            userQuestionResultVO.setPass(QuestionResult.PASS.getResult());
            userQuestionResultVO.setExeMessage(JudgeConstant.SUCCESS);
            int score = judgeSubmitDTO.getDifficulty() * JudgeConstant.DEFAULT_SCORE;
            userQuestionResultVO.setScore(score);
        } else {  // 实际输出和预期输出数量不同
            userQuestionResultVO.setScore(JudgeConstant.ERROR_SCORE);
            userQuestionResultVO.setPass(QuestionResult.ERROR.getResult());
            userQuestionResultVO.setExeMessage(CodeRunStatus.NOT_ALL_PASSED.getMessage());
        }
    }

    private void saveUserSubmit(JudgeSubmitDTO judgeSubmitDTO, UserQuestionResultVO userQuestionResultVO) {
        UserSubmit userSubmit = new UserSubmit();
        BeanUtil.copyProperties(userQuestionResultVO, userSubmit);
        userSubmit.setUserId(judgeSubmitDTO.getUserId());
        userSubmit.setExamId(judgeSubmitDTO.getExamId());
        userSubmit.setQuestionId(judgeSubmitDTO.getQuestionId());
        userSubmit.setProgramLanguage(judgeSubmitDTO.getProgramLanguage());
        userSubmit.setUserCode(judgeSubmitDTO.getUserCode());
        userSubmit.setCaseJudgeRes(JSON.toJSONString(userQuestionResultVO.getUserExeResultVOList()));
        userSubmit.setCreateBy(judgeSubmitDTO.getUserId());
        userSubmitMapper.delete(new LambdaQueryWrapper<UserSubmit>()
                .eq(UserSubmit::getUserId, judgeSubmitDTO.getUserId())
                .eq(UserSubmit::getQuestionId, judgeSubmitDTO.getQuestionId())
                .isNull(judgeSubmitDTO.getExamId() == null, UserSubmit::getExamId)
                .eq(UserSubmit::getExamId, judgeSubmitDTO.getExamId()));
        userSubmitMapper.insert(userSubmit);
    }

}
